package com.beyond.pm.base

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.beyond.common.constant.LOGIN_ROUTE
import com.beyond.common.err.UnLoginException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

abstract class BasicViewModel : ViewModel() {

    private val _loadUiIntentFlow: MutableSharedFlow<LoadUiIntent> = MutableSharedFlow()
    val loadUiIntentFlow: SharedFlow<LoadUiIntent> = _loadUiIntentFlow

    /**
     * 发送当前加载状态：Loading、Error、Normal
     */
    private fun sendLoadUiIntent(loadUiIntent: LoadUiIntent) {
        viewModelScope.launch {
            _loadUiIntentFlow.emit (loadUiIntent)
        }
    }

    open fun <T> requestLoading(
        block: suspend () -> T?,
        success: (T?) -> Unit,
        error: (Throwable) -> Unit = { sendLoadUiIntent(LoadUiIntent.Error(it.message.toString())) },
    ): Job {
        sendLoadUiIntent(LoadUiIntent.Loading(true))
        return requestNormal({
            block()
        }, {
            success(it)
        }, {
            error(it)

        })
    }

    /**
     * 过滤服务器结果，失败抛异常
     * @param block 请求体方法，必须要用suspend关键字修饰
     * @param success 成功回调
     * @param error 失败回调 可不传
     */
    private fun <T> requestNormal(
        block: suspend () -> T?,
        success: (T?) -> Unit,
        error: (Throwable) -> Unit = {}
    ): Job {
        return viewModelScope.launch {
            kotlin.runCatching {
                withContext(Dispatchers.IO) {
                    block.invoke()
                }
            }.onSuccess {
                sendLoadUiIntent(LoadUiIntent.Loading(false))
                success.invoke(it)
            }.onFailure {
                //失败回调
                it.printStackTrace()
                sendLoadUiIntent(LoadUiIntent.Loading(false))
                error.invoke(it)
                if (it is UnLoginException) {
                    sendLoadUiIntent(LoadUiIntent.Route(LOGIN_ROUTE))
                }
            }
        }
    }

}

sealed class LoadUiIntent {
    data class Loading(var isShow: Boolean) : LoadUiIntent()
    data class Error(val msg: String) : LoadUiIntent()
    data class Route(val page: String) : LoadUiIntent()
}